/**
 * 1.Promise是一个class，实例化时需传入一个函数（参数为resolve、reject），该函数会立即执行。如果执行出错，reject(error)。
 * 2.Promise的三种状态，Pending/FULFILLED/Rejected，状态的改变由resolve/reject函数完成，改变后不可逆转。
 *      resolve函数，Pending > FULFILLED
 *      reject函数，Pending > Rejected
 * 3-1.then方法，接受两个参数，分别是成功和失败的回调。根据当前状态执行相应的回调。
 *      两个参数为可选参数，默认值分别为v=>v、e=>{throw e}，将值或失败理由向后传递；
 *      两个回调参数如果执行出错，reject(error)
 * 3-2.then调用时如果状态为pending（异步操作后才改变状态），需将回调保存，在resolve或reject执行时执行回调。
 * 3-3.同一个promise对象的then方法，可以多次调用。（同步的正常，异步的cb需要使用数组存储，等到状态改变时所有cb依次执行）。
 * 3-4.then方法可链式调用，下一个then方法拿到的是当前then方法return的值：
 *      return 当前promise，循环引用是不被允许的（需放到setTimeout中做一个延迟执行，才能拿到当前promise）；
 *      return 新的promise，调用该promise的then方法即可
 *      return 其他非promise的值，resolve(v)即可
 * 4.静态方法all/race，接收一个具有Iterator接口的参数，如果参数项不是promise，会先用Promise.resolve包装。
 *      all 所有都resolve则返回resolve的值组成的有序数组，任何一个reject则reject。
 *      race 最先改变状态的一项，决定了该promise的最后状态。
 * 5.静态方法resolve，接收一个参数，返回promise
 *      参数为promise，原样返回
 *      参数为thenable的对象，包装成promise，并立即执行then方法
 *      参数为其他对象或基础类型值，resolve该参数
 * 6.静态方法reject，接受一个参数，返回Rejected 状态的promise，reason为接受的参数（原封不动）。
 * 7.catch方法，then方法，第一个参数为undefined
 * 8.finally方法，接收一个普通的回调函数（不管promise最终失败还是成功都会执行该回调），返回promise（传递value/reason）
 * 9.done方法，位于回调链尾端，抛出任何可能的错误
 */ 
const PENDING = 'Pending';
const FULFILLED = 'FULFILLED';
const REJECTED = 'Rejected';

class MyPromise {
    status = PENDING;
    value;
    reason;
    successCallbacks = [];
    failCallbacks = [];
    constructor(executor) {
        try {
            executor(this.resolve, this.reject);
        } catch (error) {
            this.reject(error)
        }
    }
    resolve = value => {
        if(this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value;
        this.successCallbacks.forEach(cb => cb());
    }
    reject = reason => {
        if(this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason;
        this.failCallbacks.forEach(cb => cb());
    }
    then(successCallback, failCallback) {
        successCallback = successCallback || (value => value);
        failCallback = failCallback || (reason => {throw reason});
        const p = new MyPromise((resolve, reject) => {
            if(this.status === FULFILLED) {
                // setTimeout 不能提取到handleThenCallback中，不然就拿不到p
                setTimeout(() => {
                    handleThenCallback(p, successCallback, this.value, resolve, reject);
                }, 0)
            }else if(this.status === REJECTED) {
                setTimeout(() => {
                    handleThenCallback(p, failCallback, this.reason, resolve, reject);
                }, 0)
            }else{
                this.successCallbacks.push(() => {
                    setTimeout(() => {
                        handleThenCallback(p, successCallback, this.value, resolve, reject);
                    }, 0)
                });
                this.failCallbacks.push(() => {
                    setTimeout(() => {
                        handleThenCallback(p, failCallback, this.reason, resolve, reject);
                    }, 0)
                });
            }
        });
        return p;
    }
    catch(failCallback) {
        return this.then(undefined, failCallback);
    }
    finally(callback) {
        return this.then(
            value => MyPromise.resolve(callback()).then(() => value), 
            reason => MyPromise.resolve(callback()).then(() => { throw reason })
        );
    }
    done(successCallback, failCallback) {
        this.then(successCallback, failCallback).catch(reason => {
            // 抛出一个全局的错误
            setTimeout(() => {
                throw reason
            }, 0);
        });
    }
    static all(array) {
        let real_array = iterator2Array(array);
        return new MyPromise((resolve, reject) => {
            let result = [], len = real_array.length;
            let addDataToResult = (index, value) => {
                result[index] = value;
                if(--len <= 0)
                    resolve(result);
            };
            real_array.forEach((item, index) => {
                item = item instanceof MyPromise ? item : MyPromise.resolve(item)
                item.then(value => addDataToResult(index, value), reason => reject(reason));
            })
        }) 
    }
    static race(array) {
        let real_array = iterator2Array(array);
        return new MyPromise((resolve, reject) => {
            real_array.forEach((item, index) => {
                item = item instanceof MyPromise ? item : MyPromise.resolve(item)
                item.then(value => resolve(value), reason => reject(reason));
            })
        })
    }
    static resolve(value) {
        if (value instanceof MyPromise)
            return value
        else if (value && typeof value.then === 'function') {
            // thenable的对象，该如何判断then方法是thenable呢？
            // let thenable = {
            //     then: (resolve, reject) => resolve(42)
            // }
        }else{
            return new MyPromise((resolve, reject) => {
                resolve(value);
            });
        }
    }
    static reject(value) {
        return new MyPromise((resolve, reject) => {
            reject(value);
        })
    }
}

/**
 * then方法失败/成功回调的执行、处理
 * @param {*} p then方法返回的新promise
 * @param {*} callback then方法传入的成功/失败回调函数
 * @param {*} data resolve的value，或reject的reason
 * @param {*} resolve 
 * @param {*} reject 
 */
function handleThenCallback(p, callback, data, resolve, reject) {
    try {
        const v = callback(data);
        handleThenCallbackReturnValue(p, v, resolve, reject);
    } catch (error) {
        reject(error)
    }
}
/**
 * 根据then方法 成功/失败回调返回值，改变新promise的状态
 * @param {*} p then方法返回的新promise
 * @param {*} v 成功或失败回调执行后返回的值
 * @param {*} resolve 
 * @param {*} reject 
 */
function handleThenCallbackReturnValue(p, v, resolve, reject) {
    if(p === v) 
        reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
    if(v instanceof MyPromise)
        v.then(resolve, reject);
    else
        resolve(v);
}

/**
 * 将具有Iterator接口转化为数组，如果参数不具有Iterator接口，抛出错误
 * @param {*} v 待转化的参数
 */
function iterator2Array(v) {
    let real_array = []
    try{
        for(let i of v)
            real_array.push(i);
    }catch(e) {
        throw new Error(e.message)
    }
    return real_array;
}

module.exports = MyPromise;

